package top.dprkshow.practice.linkedlist;

import java.util.LinkedList;

/**
 * @author Kent HAN
 * @date 2021/8/7
 */
public class MyLinkedList {
//    MyLinkedListNode head = new MyLinkedListNode();
    MyLinkedListNode head;

//    MyLinkedList() {
//        head.next = null;
//    }

    public int getSize() {
        int size = 0;
//        MyLinkedListNode tmp = new MyLinkedListNode();
//        tmp.next = head.next;
        MyLinkedListNode tmp = head;
//        while(tmp.next != null) {
        while(tmp != null) {
            size++;
            tmp = tmp.next;
        }
        return size;
    }

    public boolean isEmpty() {
//        return head.next == null;
        return head == null;
    }

    public void pushFront (int value) {
        MyLinkedListNode myLinkedListNode = new MyLinkedListNode();
        myLinkedListNode.data = value;
//        myLinkedListNode.next = head.next;
        myLinkedListNode.next = head;
//        head.next = myLinkedListNode;
        head = myLinkedListNode;
    }

    public void printDebug() {
//        System.out.println(head.next);
//        System.out.println(head);
//        MyLinkedListNode tmp = head.next;
        MyLinkedListNode tmp = head;
        while (tmp != null) {
            System.out.print(tmp.data  + " -> ");
            tmp = tmp.next;
        }
        System.out.println();
    }

    public int front() {
//        if (head.next == null) {
        if (head == null) {
            throw new RuntimeException("Cannot get front of empty list\n");
        }
//        return head.next.data;
        return head.data;
    }

    public int back() {
//        if (head.next == null) {
        if (head == null) {
            throw new RuntimeException("Cannot get back of empty list\n");
        }
//        MyLinkedListNode tmp = head.next;
        MyLinkedListNode tmp = head;
        while (tmp.next != null) {
            tmp = tmp.next;
        }
        return tmp.data;
    }

    public void popFront() {
//        if (head.next == null) {
        if (head == null) {
            throw new RuntimeException("Cannot pop front of empty list\n");
        }
//        head.next = head.next.next;
        head = head.next;
    }

    public void pushBack(int data) {
        MyLinkedListNode newNode = new MyLinkedListNode();
        newNode.data = data;
        newNode.next = null;

//        MyLinkedListNode tmp = head.next;
        MyLinkedListNode tmp = head;
        while (tmp.next != null) {
            tmp = tmp.next;
        }
        tmp.next = newNode;
    }

    public void popBack() {
        if (head == null) {
            throw new RuntimeException("Cannot pop back of empty list\n");
        }
        if (head.next == null) {
            head = null;
        } else {
            MyLinkedListNode tmp = head;
            while (tmp.next.next != null) {
                tmp = tmp.next;
            }
            tmp.next = null;
        }
    }

    public void insert(int index, int data) {
        MyLinkedListNode currNode = head, preNode = head, newNode = new MyLinkedListNode();;
        newNode.data = data;
        int currIndex = 0;

        if (head == null) {
            if (index != 0) {
                throw new RuntimeException("Out of bounds.\n");
            } else {
                newNode.next = null;
                head = newNode;
            }
        } else {
            while (currIndex < index && currNode.next != null) {
                currIndex++;
                preNode = currNode;
                currNode = currNode.next;
            }
            if (currIndex < index) {
                throw new RuntimeException("Out of bounds.\n");
            }
            newNode.next = currNode;
            preNode.next = newNode;
        }
    }

    int valueAt(int index) {
        if (head == null) {
            throw new RuntimeException("List is empty.\n");
        }
        MyLinkedListNode currNode = head;
        int currIndex = 0;
        while (currIndex < index && currNode.next != null) {
            currIndex++;
            currNode = currNode.next;
        }
        if (currIndex < index) {
            throw new RuntimeException("Index out of bounds.\n");
        }
        return currNode.data;
    }

    void erase(int index) {
        if (head == null) {
            throw new RuntimeException("List is empty.\n");
        }
        MyLinkedListNode currNode = head, preNode = head;
        int currIndex = 0;
        while (currIndex < index && currNode.next != null) {
            currIndex++;
            preNode = currNode;
            currNode = currNode.next;
        }
        if (currIndex < index) {
            throw new RuntimeException("Index out of bounds.\n");
        }
        if (index == 0) {
            head = currNode.next;
        } else {
            preNode.next = currNode.next;
        }
    }

    int nFromEnd(int n) {
        if (head == null) {
            throw new RuntimeException("List is empty.\n");
        }
        MyLinkedListNode front = head, back = head;
        for (int i = 0; i < n - 1; i++) {
            front = front.next;
            if (front == null) {
                throw new RuntimeException("Index out of bounds.\n");
            }
        }
        while (front.next != null) {
            front = front.next;
            back = back.next;
        }
        return back.data;
    }

    void reverse() {
        MyLinkedListNode prev = null, curr = head, next;
        while (curr != null) {
            next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        head = prev;
    }

    void remove(int data) {
        if (head == null) {
            throw new RuntimeException("List is empty.\n");
        }
        MyLinkedListNode pre = head, curr = head;
        while (curr.next != null) {
            if (curr.data == data) {
                if (curr == head) {
                    head = curr.next;
                } else  {
                    pre.next = curr.next;
                }
                break;
            } else {
                pre = curr;
                curr = curr.next;
            }
        }
    }

}
